<?php

namespace app\commands;

use app\models\Card;
use app\models\CardDailyUsage;
use app\models\Cron;
use app\models\Task;
use app\models\UserBill;
use Yii;
use yii\console\Controller;

/**
 * 定时任务
 * Class TaskController
 * @package app\commands
 */
class TaskController extends Controller
{
    /**
     * 每分钟执行一次
     * @throws \Exception
     */
    public function actionPerMinute()
    {
        $this->_do_task();
    }

    /**
     * 查找需要执行的定时任务
     * 当任务执行完成返回`['result' => ['_del' => true]]`时任务执行完毕自动删除
     * 当任务执行完成返回`['result' => ['_next' => 123]]`时任务将在设定的时间点执行
     * @throws \Exception
     */
    private function _do_task()
    {
        // TODO：检测上次任务执行情况（上次没有执行完成，超时等）
        Yii::info('Begin Task');
        while (true) {
            /* @var $task \app\models\Task */
            $task = Task::find()
                ->andWhere(['status' => Task::STATUS_WAITING])
                ->andWhere(['<=', 'next', time()])->one();
            if (empty($task)) {
                break;
            }
            Yii::info(print_r($task, true));
            $task->status = Task::STATUS_DOING;
            $task->save();
            $todo = json_decode($task->todo, true);
            $result = [];
            $result['begin_time'] = time();
            try {
                $result['result'] = (new \ReflectionClass($todo['class']))
                    ->getMethod($todo['method'])
                    ->invoke(null, $todo['params']);
            } catch (\Throwable $e) {
                $result['result'] = $e->getMessage();
                Yii::error($e->getMessage());
                Yii::error($e->getTraceAsString());
            }
            $result['end_time'] = time();
            $_del = false; // 是否允许执行完成后删除当前任务
            $_next = 0; // 是否有强制下次执行时间
            if (is_array($result['result'])
                && isset($result['result']['_del'])
                && $result['result']['_del'] === true) {
                $_del = true;
            }
            if (is_array($result['result'])
                && isset($result['result']['_next'])
                && is_int($result['result']['_next'])) {
                $_next = $result['result']['_next'];
            }
            $status = Task::STATUS_FINISHED;
            if ($_next > 0) {
                $next = $_next;
                $_del = false;
            } else {
                $next = $task->next;
                if (!empty($task->cron)) {
                    $_next = Cron::parse($task->cron, time() + 60);
                    if (!empty($_next)) {
                        $status = Task::STATUS_WAITING;
                        $next = $_next;
                        $_del = false; // 循环的任务不能删除
                    }
                }
            }
            if ($_del) {
                try {
                    $task->delete();
                } catch (\Throwable $t) {
                }
            } else {
                $task->status = $status;
                $task->next = $next;
                $task->result = json_encode($result);
                $task->save();
            }
        }
        // 清理老旧数据（状态完成或暂停的下次执行时间在一年前的任务）
        foreach (Task::find()
                     ->andWhere(['status' => [Task::STATUS_FINISHED, Task::STATUS_PAUSED]])
                     ->andWhere(['<=', 'next', time() - 365 * 24 * 60 * 60])
                     ->each() as $task) {
            try {
                $task->delete();
            } catch (\Throwable $t) {
            }
            Yii::info('Clear Task:' . $task->id);
        }
        Yii::info('Finish Task');
    }

    /**
     * 初始化定时任务
     */
    public function actionInit()
    {
        $this->stdout('删除旧任务：');
        $c = Task::deleteAll();
        $this->stdout($c);
        $this->stdout(PHP_EOL);

        $this->_newTask('每月1日0点1分清理当月用量', '1 0 1 * *', Card::class, 'taskClearUsage', null);
        $this->_newTask('每日1点10分刷新卡信息', '10 1 * * *', Card::class, 'taskRefresh', null);
        $this->_newTask('每日2点10分刷新卡日用量信息', '10 2 * * *', CardDailyUsage::class, 'taskRefresh', null);
        $this->_newTask('每月1号0点2分生成用户上个月空账单', '2 0 1 * *', UserBill::class, 'taskGenerateNewBill', null);
    }

    /**
     * 创建新任务
     * @param $name string 任务名称
     * @param $cron string 定时指令
     * @param $class string 类
     * @param $method string 方法
     * @param $params mixed 参数
     */
    private function _newTask($name, $cron, $class, $method, $params)
    {
        $this->stdout($name . '：');
        $task = new Task();
        $task->u_type = Task::U_TYPE_MANAGER;
        $task->uid = 1;
        $task->name = $name;
        $task->next = 0;
        $task->cron = $cron;
        $task->todo = json_encode([
            'class' => $class,
            'method' => $method,
            'params' => $params,
        ]);
        $task->status = Task::STATUS_WAITING;
        $r = $task->save();
        $this->stdout($r);
        $this->stdout(PHP_EOL);
    }
}
